/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.mochaccino.framework.smtp.client;

import com.google.common.base.Preconditions;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

/**
 * The Message encapsulate the functionnality of an email message:
 * <ul>
 * <li>From</li>
 * <li>To</li>
 * <li>cc</li>
 * <li>bcc</li>
 * <li>subject</li>
 * <li>attachments</li>
 * </ul>
 * <p/>
 * When sending an e-mail with attachment, one must choose between plain-text or
 * html because setting boths will silently favor HTML emails.
 */
public final class Message {
	/** The Attachments */
	private List<MessageAttachment> attachments = new ArrayList<MessageAttachment>();

	/** The Headers */
	private List<MessageHeader> headers = new ArrayList<MessageHeader>();

	/** The Array List */
	private List<String> recipients = new ArrayList<String>();

	/** BCC: */
	private MessageHeader bcc;

	/** CC: */
	private MessageHeader cc;

	private MessageHeader contentType = null;

	/** FROM: */
	private MessageHeader from;

	/** HTML Headers */
	private MessageHeader htmlContentTransfer = new MessageHeader( "Content-Transfer-Encoding", "quoted-printable" );
	private MessageHeader htmlContentType     = new MessageHeader( "Content-Type", "text/html; charset=UTF-8" );

	/** Mime */
	private MessageHeader mimeVersion = new MessageHeader( "MIME-Version", "1.0" );

	/** SUBJECT: */
	private MessageHeader subject;

	/** Plain Text */
	private MessageHeader textContentTransfer = new MessageHeader( "Content-Transfer-Encoding", "quoted-printable" );
	private MessageHeader textContentType     = new MessageHeader( "Content-Type", "text/plain; charset=UTF-8" );

	/** TO: */
	private MessageHeader to;

	/** HTML Content */
	private String html = null;

	/** Text Content */
	private String text = null;

	public Message() {
		this.to = new MessageHeader( "To" );
		this.from = new MessageHeader( "From" );
		this.subject = new MessageHeader( "Subject" );
		this.cc = new MessageHeader( "Cc" );
		this.bcc = new MessageHeader( "Bcc" );
	}

	public static final Message html( String from, String[] to, String subject, String html, String[] cc, String[] bcc, MessageAttachment... attachments ) {
		return multipart( from, to, subject, html, null, cc, bcc, attachments );
	}

	public static final Message multipart( String from, String[] to, String subject, String html, String text, String[] cc, String[] bcc, MessageAttachment... attachments ) {
		Preconditions.checkNotNull( from, "No From" );
		Preconditions.checkNotNull( to, "No To" );
		Preconditions.checkState( to.length > 0, "No To" );
		Preconditions.checkNotNull( subject, "No Subject" );
		Preconditions.checkState( html != null || text != null, "No Content" );

		Message msg = new Message();
		msg.setFrom( from );
		msg.setSubject( subject );

		if ( html != null ) {
			msg.setHtml( html );
		}

		if ( text != null ) {
			msg.setText( text );
		}

		for ( String c : to ) {
			msg.addTo( c );
		}

		if ( cc != null ) {
			for ( String c : cc ) {
				msg.addCc( c );
			}
		}

		if ( bcc != null ) {
			for ( String c : bcc ) {
				msg.addBcc( c );
			}
		}

		if ( attachments != null ) {
			for ( MessageAttachment a : attachments ) {
				msg.addAttachment( a );
			}
		}

		return msg;
	}

	public void addAttachment( MessageAttachment attachment ) {
		attachments.add( attachment );
	}

	/** Add BCC */
	public void addBcc( String address ) {
		Preconditions.checkNotNull( address, "Address is null..." );
		Preconditions.checkState( address.length() > 0, "Address is empty" );

		if ( bcc.size() > 0 ) {
			bcc.append( ", " );
		}
		bcc.append( address );
		recipients.add( address );
	}

	/** Add CC */
	public void addCc( String address ) {
		Preconditions.checkNotNull( address, "Address is null..." );
		Preconditions.checkState( address.length() > 0, "Address is empty" );
		if ( cc.size() > 0 ) {
			cc.append( ", " );
		}
		cc.append( address );
		recipients.add( address );
	}

	/** Add TO. */
	public void addTo( String address ) {
		Preconditions.checkNotNull( address, "Address is null..." );
		Preconditions.checkState( address.length() > 0, "Address is empty" );
		if ( to.size() > 0 ) {
			to.append( ", " );
		}
		to.append( address );
		recipients.add( address );
	}

	/** Set From */
	public void setFrom( String from ) {
		Preconditions.checkNotNull( from, "From is null..." );
		Preconditions.checkState( from.length() > 0, "From is empty" );
		this.from.set( from );
	}

	public void setHtml( String html ) {
		Preconditions.checkNotNull( html, "html is null..." );
		Preconditions.checkState( html.length() > 0, "html is empty" );
		this.html = html;
	}

	/** Set Subject */
	public void setSubject( String subject ) {
		Preconditions.checkNotNull( subject, "subject is null..." );
		Preconditions.checkState( subject.length() > 0, "subject is empty" );
		this.subject.set( subject );
	}

	public void setText( String text ) {
		Preconditions.checkNotNull( text, "text is null..." );
		Preconditions.checkState( text.length() > 0, "text is empty" );
		this.text = text;
	}

	public static final Message text( String from, String[] to, String subject, String text, String[] cc, String[] bcc, MessageAttachment... attachments ) {
		return multipart( from, to, subject, null, text, cc, bcc, attachments );
	}

	/** Add Header */
	public MessageHeader addHeaderField( String header, String value ) {
		MessageHeader h = new MessageHeader( header, value );
		headers.add( h );
		return h;
	}

	/** Add Header */
	public MessageHeader addHeaderField( String header ) {
		MessageHeader h = new MessageHeader( header );
		headers.add( h );
		return h;
	}

	public String getFrom() {
		return from.getValue();
	}

	public String[] getRecipients() {
		return recipients.toArray( new String[recipients.size()] );
	}

	public void toWriter( Writer out ) throws IOException {
		Preconditions.checkNotNull( out, "Writer is null..." );

		if ( text != null && html == null && attachments.size() > 0 ) {
			outputTextWithAttachment( out );
		} else if ( text == null && html != null && attachments.size() > 0 ) {
			outputHtmlWithAttachment( out );
		} else if ( text != null && html != null && attachments.size() > 0 ) {
			outputHtmlWithAttachment( out );
		} else if ( attachments.size() == 0 ) {
			outputTextHtml( out );
		}
	}

	private void outputHtmlWithAttachment( Writer out ) throws IOException {
		contentType = new MessageHeader( "Content-Type", "multipart/mixed; boundary=MESSAGE_CONTENTS" );
		outputHeaders( out );
		out.write( "\n" );

		outputBoundaryContentHtml( "MESSAGE_CONTENTS", out );
		outputAttachments( "MESSAGE_CONTENTS", out );

		out.write( "--MESSAGE_CONTENTS--" );
		out.write( "\n" );
		out.close();
	}

	private void outputAttachments( String boundary, Writer out ) throws IOException {
		for ( MessageAttachment attachment : attachments ) {
			out.write( "--" );
			out.write( boundary );
			out.write( "\n" );
			attachment.toWriter( out );
			out.write( "\n" );
		}
	}

	private void outputBoundaryContentHtml( String boundary, Writer out ) throws IOException {
		out.write( "--" );
		out.write( boundary );
		out.write( "\n" );
		out.write( htmlContentType.toString() );
		out.write( "\n" );
		out.write( htmlContentTransfer.toString() );
		out.write( "\n" );
		out.write( "\n" );
		out.write( html );
		out.write( "\n" );
	}

	private void outputHeaders( Writer out ) throws IOException {
		if ( mimeVersion.size() > 0 ) {
			out.write( mimeVersion.toString() );
			out.write( "\n" );
		}

		if ( contentType.size() > 0 ) {
			out.write( contentType.toString() );
			out.write( "\n" );
		}

		if ( from.size() > 0 ) {
			out.write( from.toString() );
			out.write( "\n" );
		}

		if ( to.size() > 0 ) {
			out.write( to.toString() );
			out.write( "\n" );
		}

		if ( cc.size() > 0 ) {
			out.write( cc.toString() );
			out.write( "\n" );
		}

		if ( bcc.size() > 0 ) {
			out.write( bcc.toString() );
			out.write( "\n" );
		}

		if ( subject.size() > 0 ) {
			out.write( subject.toString() );
			out.write( '\n' );
		}

		for ( MessageHeader h : headers ) {
			out.write( h.toString() );
			out.write( "\n" );
		}
	}

	private void outputTextHtml( Writer out ) throws IOException {
		contentType = new MessageHeader( "Content-Type", "multipart/alternative; boundary=MESSAGE_CONTENTS" );
		outputHeaders( out );
		out.write( "\n" );

		if ( text != null ) {
			outputBoundaryContentText( "MESSAGE_CONTENTS", out );
		}

		if ( html != null ) {
			outputBoundaryContentHtml( "MESSAGE_CONTENTS", out );
		}

		out.write( "\n" );
		out.write( "--MESSAGE_CONTENTS--" );
		out.write( "\n" );
		out.close();
	}

	private void outputBoundaryContentText( String boundary, Writer out ) throws IOException {
		out.write( "--" );
		out.write( boundary );
		out.write( "\n" );
		out.write( textContentType.toString() );
		out.write( "\n" );
		out.write( textContentTransfer.toString() );
		out.write( "\n" );
		out.write( "\n" );
		out.write( text );
		out.write( "\n" );
	}

	private void outputTextWithAttachment( Writer out ) throws IOException {
		contentType = new MessageHeader( "Content-Type", "multipart/mixed; boundary=MESSAGE_CONTENTS" );
		outputHeaders( out );
		out.write( "\n" );

		outputBoundaryContentText( "MESSAGE_CONTENTS", out );
		outputAttachments( "MESSAGE_CONTENTS", out );

		out.write( "--MESSAGE_CONTENTS--" );
		out.write( "\n" );
		out.close();
	}
}
